/*!The Treasure Box Library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (C) 2009-present, TBOOX Open Source Group.
 *
 * @author      ruki
 * @file        spinlock.h
 * @ingroup     platform
 *
 */
#ifndef TB_PLATFORM_SPINLOCK_H
#define TB_PLATFORM_SPINLOCK_H

/* //////////////////////////////////////////////////////////////////////////////////////
 * includes
 */
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <sys/time.h>

#include "atomic.h"
#include "type.h"
#include "compiler.h"
#include "keywords.h"
#include "tb_assert.h"
/* //////////////////////////////////////////////////////////////////////////////////////
 * macros
 */

// the initial value
// #define TB_SPINLOCK_INIT            TB_ATOMIC_FLAG_INIT

/* //////////////////////////////////////////////////////////////////////////////////////
 * interfaces
 */

/*! init spinlock
 *
 * @param lock      the lock
 *
 * @return          tb_true or tb_false
 */
static __tb_inline_force__ tb_bool_t tb_spinlock_init(tb_spinlock_ref_t lock)
{
    // check
    tb_assert(lock);
    return tb_true;
}

/*! exit spinlock
 *
 * @param lock      the lock
 */
static __tb_inline_force__ tb_void_t tb_spinlock_exit(tb_spinlock_ref_t lock)
{
    // check
    tb_assert(lock);
}

/*! enter spinlock
 *
 * @param lock      the lock
 */

static __tb_inline_force__ tb_void_t tb_spinlock_enter(tb_spinlock_ref_t lock)
{
    // check
    tb_assert(lock);

    // get cpu count
    // tb_size_t ncpu = tb_cpu_count();
    tb_size_t ncpu = 4;


    for ( ;; ) {

        if (*lock == 0 && atomic_cas32(lock, 0, 1)) {
            return;
        }

        if (ncpu > 1) {
            tb_size_t i, n;
            for (n = 1; n < 2048; n <<= 1) {

                for (i = 0; i < n; i++) {
                    tb_cpu_pause();
                }

                if (*lock == 0 && atomic_cas32(lock, 0, 1)) {
                    return;
                }
            }
        }

        tb_sched_yield();
    }

}

/*! try to enter spinlock
 *
 * @param lock      the lock
 *
 * @return          tb_true or tb_false
 */
static __tb_inline_force__ tb_bool_t tb_spinlock_enter_try(tb_spinlock_ref_t lock)
{
    // check
    tb_assert(lock);

    // try locking it
    return (*(lock) == 0 && atomic_cas32(lock, 0, 1));

}


/*! leave spinlock
 *
 * @param lock      the lock
 */
static __tb_inline_force__ tb_void_t tb_spinlock_leave(tb_spinlock_ref_t lock)
{
    // check
    tb_assert(lock);

    // leave
    *((int *)lock) = 0;
}

#endif
